#pragma once
#include "Event.h"
#include "RunnableTask.h"
#include "Thread.h"
#include "ThreadContext.h"
#include "Runtime/CGI/CGICommandList.h"

namespace Alice{
	bool IsInRenderingThread();
	class RenderCommand
	{
	public:
		static ENamedThreads GetDesiredThread()
		{
			return GetRenderThread();
		}

		static ESubsequentsMode GetSubsequentsMode()
		{
			return ESubsequentsMode::FireAndForget;
		}
	};
	template<typename CommandName, typename LAMBDA>
	class TEnqueueUniqueRenderCommandType : public RenderCommand
	{
	public:
		TEnqueueUniqueRenderCommandType(LAMBDA&& inLambda) : mLambda(std::forward<LAMBDA>(inLambda)) {}

		void DoTask(ENamedThreads CurrentThread, const RunnableTaskEventRef& MyCompletionGraphEvent)
		{
			CGICommandListImmediate& RHICmdList = GetImmediateCommandList_ForRenderCommand();
			mLambda(RHICmdList);
		}
	private:
		LAMBDA mLambda;
	};

	template<typename CommandName, typename LAMBDA>
	FORCEINLINE void EnqueueUniqueRenderCommand(LAMBDA&& inLambda){
		typedef TEnqueueUniqueRenderCommandType<CommandName, LAMBDA> EURCType;
		if (IsInRenderingThread()){
			inLambda(GetImmediateCommandList_ForRenderCommand());
		}else{
			TRunnableTask<EURCType>::CreateTask().ConstructAndDispatchWhenReady(std::forward<LAMBDA>(inLambda));
		}
	}

	#define ENQUEUE_RENDER_COMMAND(Type) \
	struct Type##Name \
	{  \
		static const char* cstr() { return #Type; } \
	}; \
	EnqueueUniqueRenderCommand<Type##Name>

	template<typename LAMBDA>
	FORCEINLINE void EnqueueUniqueRenderCommand(LAMBDA& Lambda){
		static_assert(sizeof(LAMBDA) == 0, "EnqueueUniqueRenderCommand enforces use of rvalue and therefore move to avoid an extra copy of the Lambda");
	}
    class RenderingThreadContext : public ThreadContext {
	public:
		SmartPtr<Event> mReadyEvent;
		RenderingThreadContext(){
			mReadyEvent = new Event;
			//Debug("RenderingThreadContext Constructor %d",mReadyEvent->mReferenceCount);
		}
		virtual ~RenderingThreadContext(){
			//Debug("~RenderingThreadContext Deconstructor %d",mReadyEvent->mReferenceCount);
		}
		virtual bool Init(void) override;
		virtual void Exit(void) override{
			Thread::RenderThreadID = 0;
		}
		virtual uint32 Run(void) override;
    };
	void StartRenderingThread();
	void StopRenderingThread();
}
